﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using yq.IBase;
using yq.IBase.DBHelper;
using yq.IBase.Basis;
using System.Reflection;
using cn.Util;
using System.Data.Common;
using System.Linq.Expressions;

namespace yq.IBase.Basis
{
    /// <summary>
    /// 缓存基础对象 ，一般model继承这个对象
    /// 继承关系 DbModelBase=> ModelBase
    /// </summary>
    /// <typeparam name="T"></typeparam>
    [Serializable]
    public abstract class ModelBase<T> : IModelBase where T : ModelBase<T>, new()
    {
        /// <summary>
        /// 语句缓存
        /// </summary>
        protected static SQLCache sqlString = new SQLCache();

        /// <summary>
        /// 字段缓存
        /// </summary>
        protected static IList<SQLProperty> sqlProperty = new List<SQLProperty>();

        /// <summary>
        /// 主键
        /// </summary>
        protected static SQLProperty FieldNameKey;

        /// <summary>
        /// 主键（内部）
        /// </summary>
        /// <returns></returns>
        public virtual string GetTableKey()
        {
            return FieldNameKey.FieldName;
        }

        /// <summary>
        /// 表名
        /// </summary>
        protected static string TableNameKey;

        /// <summary>
        /// 表名
        /// </summary>
        public virtual string GetTableName()
        {
            return TableNameKey;
        }

        /// <summary>
        /// 初始化
        /// </summary>
        static ModelBase()
        {
            var type = new T().GetType();

            //获取表名
            Object[] fromDb = type.GetCustomAttributes(typeof(TableAttribute), false);
            if (fromDb != null && fromDb.Length != 0)
            {
                TableNameKey = (fromDb[0] as TableAttribute).TableName;
            }
            else
            {
                throw new Exception("请设置表名");
            }

            PropertyInfo[] fieldinfo = type.GetProperties();

            foreach (PropertyInfo item in fieldinfo)
            {
                Object[] obj = item.GetCustomAttributes(typeof(FromDbAttribute), false);
                if (obj != null && obj.Length != 0)
                {
                    string fieldName = (obj[0] as FromDbAttribute).FieldName;
                    if (fieldName.Length == 0)
                    {
                        fieldName = item.Name;
                    }

                    var fieldType = item.PropertyType;
                    SQLProperty pr = new SQLProperty()
                    {
                        FieldName = fieldName,
                        IsKey = (obj[0] as FromDbAttribute).Iskey,
                        Type = fieldType,
                        GetLambda = _GetLambda(type, item.Name),
                        SetLambda = _SetLambda(type, item.Name, fieldType),
                        DbType = _TypeToDbType(fieldType)
                    };
                    sqlProperty.Add(pr);
                    if (pr.IsKey)
                    {
                        FieldNameKey = pr;
                    }
                }
            }


            if (FieldNameKey == null)
            {
              throw new Exception(TableNameKey+ "未设置主键");
            }
        }

     

        /// <summary>
        /// 获取列表IList
        /// </summary>
        /// <param name="dt">数据行组,应为全体字段记录</param>
        /// <returns>返回IList对象列表</returns>
        public IList<T> GetIList(DataTable dt)
        {
            IList<T> lists = new List<T>();
            if (dt == null) return lists;
            foreach (DataRow dr in dt.Rows)
            {
                T info = new T();
                SetValue(info, dr);
                lists.Add(info);
            }

            return lists;
        }

        /// <summary>
        /// 获取列表IList
        /// </summary>
        /// <param name="dt">数据集DataTable,应为全体字段记录</param>
        /// <returns>返回IList对象列表</returns>
        protected internal PageList<T> GetPageList(DataTable dt)
        {
            PageList<T> lists = new PageList<T>();

            if (dt == null) return lists;
            foreach (DataRow dr in dt.Rows)
            {
                T info = new T();
                SetValue(info, dr);
                lists.Add(info);
            }

            return lists;
        }

        /// <summary>
        /// 根据主键获取选择性的SQL语句
        /// </summary>
        /// <param name="id">主键值</param>
        /// <returns></returns>
        protected internal virtual string GetSelectKey(string id)
        {
            var sql = GetSelectSql();
            sql.WhereString = " Where " + GetTableKey() + " = @id ";

            return sql.toSql();
        }

        /// <summary>
        /// 根据主键获取选择性的SQL语句
        /// </summary>
        /// <param name="id">主键值</param>
        /// <returns></returns>
        protected internal virtual string GetSelectKey(IList<string> id)
        {
            string strid = string.Join("','", id);
            var sql = GetSelectSql();
            sql.WhereString = " Where " + GetTableKey() + " in ('" + strid + "')";

            return sql.toSql();
        }

        /// <summary>
        /// 返回Select所有字段的SQL语句,不带From
        /// </summary>
        /// <returns></returns>
        protected internal string GetSelectColumns()
        {
            if (string.IsNullOrEmpty(sqlString.Select))
            {
                StringBuilder sql = new StringBuilder();
                foreach (var info in GetColumns())
                {
                    if (sql.ToString().Length == 0)
                    {
                        sql.Append(info);
                    }
                    else
                    {
                        sql.Append("," + info);
                    }
                }
                sqlString.Select = sql.ToString();
            }

            return sqlString.Select;
        }

        /// <summary>
        /// 返回Insert所有字段的SQL语句
        /// </summary>
        /// <param name="tableName"></param>
        /// <param name="IsKey">是否包含主键</param>
        /// <returns></returns>
        protected internal string GetInsertColumns(string tableName = null, bool IsKey = true)
        {
            if (string.IsNullOrEmpty(sqlString.Insert))
            {
                StringBuilder sql = new StringBuilder();
                StringBuilder sqlInsert = new StringBuilder();
                StringBuilder sqlValues = new StringBuilder();

                foreach (var code in GetColumns())
                {
                    if (IsKey == false && code == GetTableKey())
                    {
                        continue;
                    }

                    if (sqlInsert.ToString().Length == 0)
                    {
                        sqlInsert.Append(code);
                    }
                    else
                    {
                        sqlInsert.Append("," + code);
                    }

                    if (sqlValues.ToString().Length == 0)
                    {
                        sqlValues.Append("@" + code);
                    }
                    else
                    {
                        sqlValues.Append(",@" + code);
                    }
                }
                sql.Append(" Insert Into " + (tableName ?? GetTableName()) + "(");
                sql.Append(sqlInsert.ToString());
                sql.Append(") Values(");
                sql.Append(sqlValues.ToString());
                sql.Append(")");

                sqlString.Insert = sql.ToString();
            }

            return sqlString.Insert;
        }

        /// <summary>
        /// 返回Update所有字段的SQL语句
        /// </summary>
        /// <returns></returns>
        protected internal string GetUpdateColumns(string tableName = null)
        {
            if (string.IsNullOrEmpty(sqlString.Update))
            {
                StringBuilder sql = new StringBuilder();
                sql.Append(" Update " + (tableName ?? GetTableName()) + " Set ");

                StringBuilder sqlInsert = new StringBuilder();
                foreach (var code in GetColumns())
                {
                    if (code != GetTableKey())
                    {
                        if (sqlInsert.ToString().Length == 0)
                        {
                            sqlInsert.Append(code + "=@" + code);
                        }
                        else
                        {
                            sqlInsert.Append("," + code + "=@" + code);
                        }
                    }
                }
                sql.Append(sqlInsert.ToString());

                sql.Append(" Where " + GetTableKey() + "=@" + GetTableKey());

                if (GetColumns().Count(obj => obj.ToLower() == "version") != 0)
                {
                    //版本号验证
                    sql.Append(" and Version=@_versionOdl ");
                }

                sqlString.Update = sql.ToString();
            }

            return sqlString.Update;
        }



        /// <summary>
        /// 返回Select 的Where之前(不带where关键字)的SQL语句
        /// </summary>
        /// <param name="sqlWhere">SQL条件,必须从where开始,可以为空</param>
        /// <returns></returns>
        protected internal SelectSQL GetSelectSql(SelectSQL sql = null)
        {
            if (sql == null)
            {
                sql = new SelectSQL();
            }
            sql.Field = GetSelectColumns();
            sql.Table = string.IsNullOrEmpty(sql.Table) ? GetTableName() : sql.Table;

            return sql;
        }

        /// <summary>
        /// 返回Select count(1) 的Where之前(不带where关键字)的SQL语句
        /// </summary>
        /// <param name="sql">SQL条件,必须从where开始,可以为空</param>
        /// <returns></returns>
        protected internal SelectSQL GetSelectCountSql(SelectSQL sql)
        {
            if (string.IsNullOrEmpty(sql.Table))
            {
                sql.Table = GetTableName();
            }
            sql.Field = " count(1) ";

            return sql;
        }

        /// <summary>
        /// 让model 不需要实例化也能调用
        /// </summary>
        public static T Static
        {
            get
            {
                return new T();
            }
        }

        /// <summary>
        /// 加载数据
        /// </summary>
        /// <param name="id"></param>
        /// <param name="db"></param>
        /// <returns></returns>
        public T Load(string id, DbHelper db=null)
        {
            T t = new T();
            //用完是否关闭
            bool isClose = db == null;
            try
            {
                db = db ?? new DbHelper();
                var cm = db.GetSqlStringCommond(t.GetSelectKey(id));
                db.AddInParameter(cm, "@id", DbType.String, id);
                DataTable dt = db.ExecuteDataTable(cm);
                if (dt.Rows.Count != 0)
                {
                    t.SetValue(t, dt.Rows[0]);
                }
            }
            finally
            {
                if (isClose)
                {
                    db.Dispose();
                }
            }

            return t ?? new T();
        }


        /// <summary>
        /// 字段列表
        /// </summary>
        protected  List<string> GetColumns()
        {
            return sqlProperty.OrderBy(obj => obj.IsKey).Select(obj => obj.FieldName).ToList();
        }


        /// <summary>
        /// 写入主键值
        /// </summary>
        /// <param name="Id">主键</param>
        public  void SetKeyValue(dynamic Id)
        {
            FieldNameKey.SetLambda(this, Id);
        }

        /// <summary>
        /// 获取主键值
        /// </summary>
        /// <returns></returns>
        public  dynamic GetKeyValue()
        {
            return FieldNameKey.GetLambda(this); ;
        }

        /// <summary>
        /// 写入新Version
        /// </summary>
        public void SetVersionNewGuid()
        {
            foreach (var info in sqlProperty)
            {
                if (info.FieldName == "Version")
                {
                    info.SetLambda(this, Guid.NewGuid().ToString());
                }
            }
        }



        public void SetValue(T info, DataRow row)
        {
            if (row == null)
            {
                info.IsLoad = false;
            }
            else
            {
                info.IsLoad = true;
                foreach (var key in sqlProperty)
                {
                    if (row[key.FieldName] != DBNull.Value)
                    {
                        if (key.Type == typeof(int))
                        {
                            key.SetLambda(info,Convert.ToInt32(row[key.FieldName]));
                        }
                        else if (key.Type == typeof(decimal)) 
                        {
                            key.SetLambda(info,Convert.ToDecimal(row[key.FieldName]));
                        }
                        else if (key.Type == typeof(DateTime)) 
                        {
                            key.SetLambda(info, Convert.ToDateTime(row[key.FieldName]));
                        }
                        else
                        {
                            key.SetLambda(info, row[key.FieldName].ToString());
                        }
                    }
                }
            }

        }

        public  void GetDbParameters(DbCommand comm, T info, DbHelper cn)
        {
            info.IsLoad = true;
            foreach (var key in sqlProperty)
            {
                if (key.FieldName != "Version")
                {
                    cn.AddInParameter(comm, "@" + key.FieldName, key.DbType, ParameterTool.GetObj(key.GetLambda(info)));
                }
                else
                {
                    cn.AddInParameter(comm, "@_versionOdl", DbType.String, key.GetLambda(info));
                    cn.AddInParameter(comm, "@Version", DbType.String, Guid.NewGuid().ToString());
                }
            }
        }


        /// <summary>
        /// 获取值委托
        /// </summary>
        /// <param name="type"></param>
        /// <param name="fieldname"></param>
        /// <returns></returns>
        private static Func<object, object> _GetLambda(Type type, string fieldname)
        {
            var p = type.GetProperty(fieldname);
            //lambda的参数u 
            var param_obj = Expression.Parameter(typeof(object), "obj");
            //类型转换
            var convert_obj = Expression.Convert(param_obj, type);
            //lambda的方法体 ((User)obj).Age
            var pGetter = Expression.Property(convert_obj, p);
            //对返回值进行类型转换
            var returnObj = Expression.Convert(pGetter, typeof(object));
            //编译lambda 
            return Expression.Lambda<Func<object, object>>(returnObj, param_obj).Compile();
        }

        /// <summary>
        /// 写入值委托
        /// </summary>
        /// <param name="type"></param>
        /// <param name="fieldType"></param>
        /// <returns></returns>
        private static Action<object, object> _SetLambda(Type type, string fieldname, Type fieldType)
        {
            var p = type.GetProperty(fieldname);
            //对象实例
            var param_obj = Expression.Parameter(typeof(object), "obj");
            //值
            var param_val = Expression.Parameter(typeof(object), "val");
            //转换参数为真实类型
            var body_obj = Expression.Convert(param_obj, type);
            var body_val = Expression.Convert(param_val, fieldType);
            //调用给属性赋值的方法
            var body = Expression.Call(body_obj, p.GetSetMethod(), body_val);
            return Expression.Lambda<Action<object, object>>(body, param_obj, param_val).Compile();
        }

        private static DbType _TypeToDbType(Type t)
        {
            DbType dbt;
            try
            {
                dbt = (DbType)Enum.Parse(typeof(DbType), t.Name);
            }
            catch
            {
                dbt = DbType.Object;
            }
            return dbt;
        }

    }


    /// <summary>
    /// sql语句缓存
    /// </summary>
    public class SQLCache
    {
        public string Insert { get; set; }

        public string Update { get; set; }

        public string Select { get; set; }

        public string TableName { get; set; }

        public string TableKye { get; set; }
    }

    public class SQLProperty
    {
        /// <summary>
        /// 字段名称
        /// </summary>
        public string FieldName { get; set; }

        /// <summary>
        /// 是否主键
        /// </summary>
        public bool IsKey { get; set; }

        /// <summary>
        /// 字段类型
        /// </summary>
        public Type Type { get; set; }

        /// <summary>
        /// 字段类型
        /// </summary>
        public DbType DbType { get; set; }

        
        /// <summary>
        /// Set方法
        /// </summary>
        public Action<object, object> SetLambda { get; set; }

        /// <summary>
        /// Get方法
        /// </summary>
        public Func<object, object> GetLambda { get; set; }
    }

}
